毫秒级检测!你见过带GPU的树莓派吗? | 您所在的位置:网站首页 › 树莓派3b 超频 › 毫秒级检测!你见过带GPU的树莓派吗? |
树莓派3B+英特尔神经计算棒进行高速目标检测 转载请注明作者梦里茶 代码: 训练数据预处理: https://gist.github.com/ahangchen/ae1b7562c1f93fdad1de58020e94fbdf 测试:https://github.com/ahangchen/ncs_detection Star是一种美德。 Background最近在做一个项目,要在树莓派上分析视频中的图片,检测目标,统计目标个数,这是一张样例图片: Motivation当下效果最好的目标检测都是基于神经网络来做的,包括faster rcnn, ssd, yolo2等等,要在树莓派这种资源紧张的设备上运行检测模型,首先想到的就是用最轻量的MobileNet SSD,使用Tensorflow object detection api实现的MobileNet SSD虽然已经非常轻,但在树莓派上推导一张1280x720的图仍然需要2秒,有兴趣的同学可以参考这两个项目: armv7版Tensorflow(必须是1.4及以上):https://github.com/lhelontra/tensorflow-on-arm/releasesTensorflow Object detection API: https://github.com/tensorflow/models/tree/master/research/object_detection具体的操作在Tensorflow文档里都说的很清楚了,在树莓派上的操作也是一样的,有问题可以评论区讨论 Hardware极限的模型仍然不能满足性能需求,就需要请出我们今天的主角了,Intel Movidius Neural Computing Stick 处理器 Intel Movidius VPU 支持框架 TensorFlow, Caffe 连接方式 USB 3.0 Type-A 尺寸 USB stick (72.5mm X 27mm X 14mm) 工作温度 0° - 40° C x86_64 Ubuntu 16.04主机 Raspberry Pi 3B Stretch desktop Ubuntu 16.04 虚拟机 系统要求 USB 2.0 以上 (推荐 USB 3.0) 1GB 内存 4GB 存储 实际上这不是一个GPU,而是一个专用计算芯片,但能起到类似GPU对神经网络运算的加速作用。 京东上搜名字可以买到,只要500元左右,想想一块GPU都要几千块钱,就会觉得很值了。 SDK是开源的:https://github.com/movidius/ncsdk 提问不在GitHub issue里,而是在一个专门的论坛:https://ncsforum.movidius.com/ 虽然目前NCSDK支持的框架包含Tensorflow和Caffe,但并不是支持所有的模型,目前已支持的模型列表可以在这里查到:https://github.com/movidius/ncsdk/releases 截止到2018年3月15日,NCSDK还没有支持Tensorflow版的MobileNet SSD(比如tf.cast这个操作还未被支持),所以我们需要用Caffe来训练模型,部署到树莓派上。 Environmentncsdk的环境分为两部分,训练端和测试端。 训练端通常是一个Ubuntu 带GPU主机,训练Caffe或TensorFlow模型,编译成NCS可以执行的graph;测试端则面向ncs python mvnc api编程,可以运行在树莓派上raspbian stretch版本,也可以运行在训练端这种机器上。训练端安装安装这个过程,说难不难,也就几行命令的事情,但也有很多坑 在训练端主机上,插入神经计算棒,然后: git clone https://github.com/movidius/ncsdk cd ncsdk make install其中,make install干的是这些事情: 检查安装Tensorflow检查安装Caffe(SSD-caffe)编译安装ncsdk(不包含inference模块,只包含mvNCCompile相关模块,用来将Caffe或Tensorflow模型转成NCS graph的)注意, 这些库都是安装到/opt/movidius/这个目录下,并关联到系统python3里边的(/usr/bin/python3),如果你电脑里原来有tf或caffe,也不会被关联上去NCSDK mvNCCompile模块目前只兼容python3,我尝试过将安装完的SDK改成兼容python2的版本,可以将模型编译出来,但是在运行时会报错,所以暂时放弃兼容python2了,也建议大家用默认的python3版本这个步骤主要的坑来自万恶的Caffe,如果你装过python3版的caffe,大概会有经验一些,这里有几个小坑提示一下:最好在ncsdk目录中的ncsdk.conf中,开启caffe的cuda支持,即设置CAFFE_USE_CUDA=yes,这样你之后也能用这个caffe来训练模型caffe的依赖会在脚本中安装,但有些Debian兼容问题要解决开启CUDA支持后,编译caffe会找不到libboost-python3,因为在Ubuntu16.04里,它叫libboost-python3.5,所以要软链接一下:cd /usr/lib/x86_64-linux-gnu/ sudo ln -s libboost_python-py35.so libboost_python3.so其他可能出现的caffe的坑,可以在我博客找找答案,如果没有的话,就去caffe的GitHub issue搜吧测试一波操作之后,我们装好了ncsdk编译模块,可以下载我训练的caffe模型,尝试编译成ncs graph git clone https://github.com/ahangchen/MobileNetSSD mvNCCompile example/MobileNetSSD_deploy.prototxt -w MobileNetSSD_deploy.caffemodel -s 12 -is 300 300 -o ncs_mobilenet_ssd_graph这里其实是调用python3去执行/usr/local/bin/ncsdk/mvNCCompile.py这个文件, 不出意外在当前版本(1.12.00)你会遇到这个错误: [Error 17] Toolkit Error: Internal Error: Could not build graph. Missing link: conv11_mbox_conf这是因为NCSDK在处理caffe模型的时候,会把conv11_mbox_conf_new节点叫做conv11_mbox_conf,所以build graph的时候就会找不着。因此需要为这种节点起一个别名,即,将conv11_mbox_conf_new起别名为conv11_mbox_conf,修改SDK代码中的/usr/local/bin/ncsdk/Models/NetworkStage.py,在第85行后面添加: if ''_new' in name: self.alias.append(name[:-4])于是就能编译生成graph了,你会看到一个名为ncs_mobilenet_ssd_graph的文件。 上边这个bug我已经跟NCSDK的工程师讲了,他们在跟进修这个bug: 测试端NCSDK测试端要安装ncsdk python api,用于inference,实际上测试端能做的操作,训练端也都能做 git clone https://github.com/movidius/ncsdk cd api/src make install从输出日志可以发现,将ncsdk的lib和include文件分别和系统的python2(/usr/bin/python2)和python3(/usr/bin/python3)做了关联。 然后你可以下一个GitHub工程来跑一些测试: git clone https://github.com/movidius/ncappzoo cd ncappzoo/apps/hello_ncs_py python3 hello_ncs.py python2 hello_ncs.py没报错就是装好了,测试端很简单。 OpenCV看pyimagesearch这个教程 Caffe模型训练就是正常的用caffe训练MobileNet-SSD,主要参考这个仓库: MobileNet-SSD: https://github.com/chuanqi305/MobileNet-SSDREADME里将步骤讲得很清楚了 下载SSD-caffe(这个我们已经在NCSDK里装了)下载chuanqi在VOC0712上预训练的模型把MobileNet-SSD这个项目放到SSD-Caffe的examples目录下,这一步可以不做,但是要对应修改train.sh里的caffe目录位置创建你自己的labelmap.prototxt,放到MobileNet-SSD目录下,比如说,你是在coco预训练模型上训练的话,可以把coco的标签文件复制过来,将其中与你的目标类(比如我的目标类是Cattle)相近的类(比如Coco中是Cow)改成对应的名字,并用它的label作为你的目标类的label。(比如我用21这个类代表Cattle)用你自己的数据训练MobileNet-SSD,参考SSD-caffe的wiki,主要思路还是把你的数据转换成类似VOC或者COCO的格式,然后生成lmdb,坑也挺多的:假设你的打的标签是这样一个文件raw_label.txt,假装我们数据集只有两张图片:data/strange_animal/1017.jpg 0.487500 0.320675 0.670000 0.433193 data/strange_animal/1018.jpg 0.215000 0.293952 0.617500 0.481013我们的目标是将标签中涉及的图片和位置信息转成这样一个目录(在ssd-caffe/data/coco目录基础上生成的): coco_cattle ├── all # 存放全部图片和xml标签文件 │ ├── 1017.jpg │ ├── 1017.xml │ ├── 1018.jpg │ └── 1018.xml ├── Annotations # 存放全部标签xml │ ├── 1017.xml │ └── 1018.xml ├── create_data.sh # 将图片转为lmdb的脚本 ├── create_list.py # 根据ImageSets里的数据集划分文件,生成jpg和xml的对应关系文件到coco_cattle目录下,但我发现这个对应关系文件用不上 ├── images # 存放全部图片 │ ├── 1017.jpg │ └── 1018.jpg ├── ImageSets # 划分训练集,验证集和测试集等,如果只想分训练和验证的话,可以把minival.txt,testdev.txt,test.txt内容改成一样的 │ ├── minival.txt │ ├── testdev.txt │ ├── test.txt │ └── train.txt ├── labelmap_coco.prototxt # 如前所述的标签文件,改一下可以放到MobileNet-SSD目录下 ├── labels.txt ├── lmdb # 手动创建这个目录 │ ├── coco_cattle_minival_lmdb # 自动创建的,由图片和标签转换来的LMDB文件 │ ├── coco_cattle_testdev_lmdb │ ├── coco_cattle_test_lmdb │ └── coco_cattle_train_lmdb ├── minival.log ├── README.md ├── testdev.log ├── test.log └── train.log其中,标签xml的格式如下: train 86 coco_cattle 720 1280 3 0 21 Unspecified 0 0 169 388 372 559 21 Unspecified 0 0 169 388 372 559代表一张图中多个对象所在位置(bndbox节点表示),以及类别(name)。 一开始,all, Annotations, images, ImageSets,lmdb四个目录都是空的,你可以把自己的图片放到随便哪个地方,只要在raw_label.txt里写好图片路径就行读取raw_label.txt,利用lxml构造一棵dom tree,然后写到Annotations对应的xml里,并将对应的图片移动到image目录里,可以参考这份代码。并根据我们设置的train or not标志符将当前这张图片分配到训练集或测试集中(也就是往ImageSet/train.txt中写对应的图片名)这样一波操作之后,我们的images和Annotations目录里都会有数据了,接下来我们需要把它们一块复制到all目录下cp images/* all/ cp Annotations/* all/然后用create_data.sh将all中的数据,根据ImageSet中的数据集划分,创建训练集和测试集的lmdb,这里对coco的create_data.sh做了一点修改:cur_dir=$(cd $( dirname ${BASH_SOURCE[0]} ) && pwd ) root_dir=$cur_dir/../.. cd $root_dir redo=true # 这里改成all目录 data_root_dir="$cur_dir/all" # 这里改成自己的数据集名,也是我们这个目录的名字 dataset_name="coco_cattle" # 指定标签文件 mapfile="$root_dir/data/$dataset_name/labelmap_coco.prototxt" anno_type="detection" label_type="xml" db="lmdb" min_dim=0 max_dim=0 width=0 height=0 extra_cmd="--encode-type=jpg --encoded" if $redo then extra_cmd="$extra_cmd --redo" fi for subset in minival testdev train test do python3 $root_dir/scripts/create_annoset.py --anno-type=$anno_type --label-type=$label_type --label-map-file=$mapfile --min-dim=$min_dim --max-dim=$max_dim --resize-width=$width --resize-height=$height --check-label $extra_cmd $data_root_dir $root_dir/data/$dataset_name/ImageSets/$subset.txt $data_root_dir/../$db/$dataset_name"_"$subset"_"$db examples/$dataset_name 2>&1 | tee $root_dir/data/$dataset_name/$subset.log done于是会lmdb目录下会为每个划分集合创建一个目录,存放数据 ├── lmdb │ ├── coco_cattle_minival_lmdb │ │ ├── data.mdb │ │ └── lock.mdb │ ├── coco_cattle_testdev_lmdb │ │ ├── data.mdb │ │ └── lock.mdb │ ├── coco_cattle_test_lmdb │ │ ├── data.mdb │ │ └── lock.mdb │ └── coco_cattle_train_lmdb │ ├── data.mdb │ └── lock.mdb将5生成的lmdb链接到MobileNet-SSD的目录下:cd MobileNet-SSD ln -s PATH_TO_YOUR_TRAIN_LMDB trainval_lmdb ln -s PATH_TO_YOUR_TEST_LMDB test_lmdb运行gen_model.sh生成三个prototxt(train, test, deploy)# 默认clone下来的目录是没有example这个目录的,而gen_model.sh又会把文件生成到example目录 mkdir example ./gen_model.sh训练 ./train.sh 这里如果爆显存了,可以到example/MobileNetSSD_train.prototxt修改batch size,假如你batch size改到20,刚好可以吃满GTX1060的6G显存,但是跑到一定步数(设置在solver_test.prototxt里的test_interval变量),会执行另一个小batch的test(这个batch size定义在example/MobileNetSSD_test.prototxt里),这样就会再爆显存,所以如果你的train_batch_size + test_batch_size score_thresh: boxes.append(box) scores.append(score) classes.append(cl) num += 1 return num, boxes, classes, scores用OpenCV将当前图片的对象数量写到图片右上角,用pillow(tf库中的实现)将当前图片的对象位置和类别在图中标出def add_str_on_img(image, total_cnt): cv2.putText(image, '%d' % total_cnt, (image.shape[1] - 100, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)result = vis_util.visualize_boxes_and_labels_on_image_array( image_np, np.squeeze(valid_boxes).reshape(num, 4), np.squeeze(valid_classes).astype(np.int32).reshape(num, ), np.squeeze(valid_scores).reshape(num, ), category_index, use_normalized_coordinates=True, min_score_thresh=score_thresh, line_thickness=8)保存图片 cv2.imwrite('%s_tmp/%s' % (dataset_pref, image_path.split('/')[-1]), cv2.cvtColor(result, cv2.COLOR_RGB2BGR))释放神经计算棒def ncs_clean(detection_graph, device): detection_graph.DeallocateGraph() device.CloseDevice()运行python2 ncs_detection.py 结果框架 图片数量/张 耗时 TensorFlow 1800 60min NCS 1800 10min TensorFlow 1 2sec NCS 1 0.3sec 性能提升6倍!单张图300毫秒,可以说是毫秒级检测了。在论坛上有霓虹国的同行尝试后,甚至评价其为“超爆速”。 扩展单根NCS一次只能运行一个模型,但是我们可以用多根NCS,多线程做检测,达到更高的速度,具体可以看Reference第二条。 Referencehttps://www.pyimagesearch.com/2018/02/19/real-time-object-detection-on-the-raspberry-pi-with-the-movidius-ncs/https://qiita.com/PINTO/items/b97b3334ed452cb555e2看了这么久,还不快去给我的GitHub点star! |
CopyRight 2018-2019 实验室设备网 版权所有 |